<?php

/**
 * Manages in-/output of data
 * @since  0.2.0
 *
 * Assertions are predicates which shall be true in the moment they are
 * placed. A developer knows where when which variable has what content
 * and with this knowledge you can check if the program is working right
 * by defining predicates.
 * 
 * If an assertion fails it will send an errormessage to the user or some
 * other kind of output defined by Assert::setOutput.
 * 
 * \note{ PHP has already the function
 *        <a href="http://php.net/manual/function.assert.php">assert</a>,
 *        Comparing to the implemented function assert this class handle
 *        objects and values very precise which will give you more static
 *        functionality (known from C++\, C#\, Eiffel\, Fortran\, Java\, ML\,
 *        Pascal\, Perl\, ...)
 *        instead of the weak typing which php gives (e.g. "a" == 97 == TRUE). 
 * }
 *
 * @section Examples
 * 
 * @subsection Assert_Simple-predicate Simple predicate
 * @code
 * <?php
 * 
 * // do something
 * $x = 5;
 * $y = 2;
 * $z = $x / $y;
 * 
 * // predicate what should be
 * Assert::isTrue($z == 2.5);
 * 
 * ?>
 * @endcode
 * 
 * @subsection Assert_Force-predicate Force predicate
 * @code
 * <?php
 * 
 * // some input
 * $x = intval("19.90" * 100);
 * 
 * // check if the multiplication failed
 * if (Assert::isFalse($x == 1990))
 * {
 *   // if so then force the right solution
 *   $x = 1990;
 * }
 * 
 * ?>
 * @endcode
 * 
 * \note{ Checking only $x != 1990 is NOT the same,
 *        Assertions outputs a message while a simple "if ($x != 1990)" does
 *        nothing. An assert is more like this:
 * @code
 * if ($x != 1990) {
 * 	echo "Assertion of x failed";		// this does Assert
 * 	$x = 1990;
 * }
 * @endcode
 * }
 *
 * @see Debug
 *
 */
class Assert {
	public static $generateOutput = true;
	
	private static $_output = NULL;
	
	static protected function _assert($x, $y)
	{
		return ($x == $y);
	}
	
	/**
	 * Checks if given value is true
	 * @since 0.2.0
	 * @param \c boolean $boolean
	 */	
	static public function isTrue($boolean, $message = NULL)
	{
		$assert = Assert::_assert($boolean, TRUE);
		
		if (!$assert) self::_output(__FUNCTION__, $message);
		return $assert;
	}
	
	/**
	 * Checks if given value is false
	 * @since 0.2.0
	 * @param \c boolean $boolean
	 */
	static public function isFalse($boolean, $message = NULL)
	{
		$assert = self::isTrue($boolean === FALSE);
		
		if (!$assert) self::_output(__FUNCTION__, $message);
		return $assert;
	}
	
	/**
	 * Checks if given value is an array
	 * @since 0.2.0
	 * @param \c mixed $variable
	 */
	static public function isArray($variable, $message = NULL)
	{
		$assert = (Dump::getType($variable) == DUMP_TYPE_ARRAY);
		
		if (!$assert) self::_output(__FUNCTION__, $message);
		return $assert;
	}
	
	/**
	 * Checks if given value is an array
	 * @since 0.2.0
	 * @param \c mixed $variable
	 * @todo D110321 examples
	 */
	static public function isBoolean($variable, $message = NULL)
	{
		$assert = (Dump::getType($variable) == DUMP_TYPE_BOOLEAN);
		
		if (!$assert) self::_output(__FUNCTION__, $message);
		return $assert;
	}
	
	/**
	 * Checks if given value is empty
	 * @since 0.2.0
	 * @param \c mixed $variable
	 * @todo D110321 examples
	 */
	static public function isEmpty($variable, $message = NULL)
	{
		$assert = (empty($variable) && $variable != "0");
		
		if (!$assert) self::_output(__FUNCTION__, $message);
		return $assert;
	}
	
	/**
	 * Checks if given value is a float-value
	 * @since 0.2.0
	 * @param \c mixed $variable
	 * @todo D110321 examples
	 */
	static public function isFloat($variable, $message = NULL)
	{
		$assert = (Dump::getType($variable) == DUMP_TYPE_FLOAT);
		
		if (!$assert) self::_output(__FUNCTION__, $message);
		return $assert;
	}
	
	/**
	 * Checks if given value is an integer
	 * @since 0.2.0
	 * @param \c mixed $variable
	 * @todo D110321 examples
	 */
	static public function isInteger($variable, $message = NULL)
	{
		$assert = (Dump::getType($variable) == DUMP_TYPE_INTEGER);
		
		if (!$assert) self::_output(__FUNCTION__, $message);
		return $assert;
	}
	
	/**
	 * Checks if given value is null
	 * @since 0.2.0
	 * @param \c mixed $variable
	 * @todo D110321 examples
	 */
	static public function isNull($variable, $message = NULL)
	{
		$assert = (Dump::getType($variable) == DUMP_TYPE_NULL);
		
		if (!$assert) self::_output(__FUNCTION__, $message);
		return $assert;
	}
	
	/**
	 * Checks if given value is an object
	 * @since 0.2.0
	 * @param \c mixed $variable
	 * @todo D110321 examples
	 */
	static public function isObject($variable, $message = NULL)
	{
		$assert = (Dump::getType($variable) == DUMP_TYPE_OBJECT);
		
		if (!$assert) self::_output(__FUNCTION__, $message);
		return $assert;
	}
	
	/**
	 * Checks if given value is a resource
	 * @since 0.2.0
	 * @param \c mixed $variable
	 * @todo D110321 examples
	 */
	static public function isResource($variable, $message = NULL)
	{
		$assert = (Dump::getType($variable) == DUMP_TYPE_RESOURCE);
		
		if (!$assert) self::_output(__FUNCTION__, $message);
		return $assert;
	}
	
	/**
	 * Checks if given value is a string
	 * @since 0.2.0
	 * @param \c mixed $variable
	 * @todo D110321 examples
	 */
	static public function isString($variable, $message = NULL)
	{
		$assert = (Dump::getType($variable) == DUMP_TYPE_STRING);
		
		if (!$assert) self::_output(__FUNCTION__, $message);
		return $assert;
	}
	
	static public function isDbLink($variable, $message = NULL)
	{
		// is it even a resource?
		$assert = (Dump::getType($variable) == DUMP_TYPE_RESOURCE);
		
		// only change if it's not in the list
		if ($assert)
		{
			switch (get_resource_type($variable)) {
				case "mysql link":
				break;
				
				default:
					$assert = false;
				break;
			}
		}
		
		if (!$assert) self::_output(__FUNCTION__, $message);
		return $assert;
	}
	
	/**
	 * Casts given value on boolean
	 * @since 0.2.0
	 * @param \c mixed $variable
	 * @todo D110321 examples
	 */
	static public function castBoolean(&$variable)
	{
		$variable = (bool) (is_scalar($variable)? $variable: FALSE);
	}
	
	/**
	 * Casts given value on integer
	 * @since 0.2.0
	 * @param \c mixed $variable
	 * @todo D110321 examples
	 */
	static public function castInteger(&$variable)
	{
		$variable = (int) (is_scalar($variable)? $var: 0);
	}
	
	/**
	 * Casts given value on float
	 * @since 0.2.0
	 * @param \c mixed $variable
	 * @todo D110321 examples
	 */
	static public function castFloat(&$variable)
	{
		$variable = (float) (is_scalar($variable)? $var: 0);
	}
	
	/**
	 * Set the callback if assert fails
	 * @since 0.2.0
	 * @param \c callback $callback A function or method to write messages to
	 * @todo D110321 examples
	 */
	static public function setOutput($callback = NULL)
	{
		self::$_output = $callback;
	}
	
	static private function _output($source, $user_message)
	{
		if (self::$generateOutput && self::$_output == NULL)
		{
			$a = debug_backtrace();
			$file = $a[1]["file"];
			$file = str_replace("\\", "/", $file);
			$file = "\"" . str_replace($_SERVER["DOCUMENT_ROOT"], "", $file);
			
			$error = 'Assertion' . ($user_message === NULL)?
			                        '"' . $source . '" failed':
			                        ": " . $user_message;
				
			if (self::$_output === NULL)
			{
				trigger_error($error . ' in file '
				             . $file . '", line' . $a[1]["line"], E_USER_NOTICE);
			}
			else
			{
				self::$_output($file, $a[1]["line"], $message);
			}
		}
	}
}